/*
1. 逻辑符号： && 表示"且"关系、 || 表示"或"关系、 ( ) 表示条件组；
2. 匹配符号： == 表示"精准匹配"、 = 表示"匹配"、 =^ 表示"开始为"、 =$ 表示"结尾为"、 =* 通配符匹配、 =*！非通配符匹配、 =! 表示"不匹配"、 ==! 表示"精准不匹配"、 =!^ 表示"开始不为"、 =!$ 表示"结尾不为"、 === 表示"模糊匹配" ；
3. 包含与模糊匹配有所区别，包含匹配的是词语，而模糊匹配匹配的是逐个字符。如 "login page" 与 "login pa"；
其中优先级遵循逻辑判断优先级
*/

class AdvancedSearch {
    matcherReg = /^(==|=|=|=\^|=\$|=!|=\*!|=\*|==!|=!\^|=!\$|===)/
    isTempReg = /^.+(==|=|=|=\^|=\$|=!|=\*!|=\*|==!|=!\^|=!\$|===).*$/
    constructor() {}
  
    /**
     * 校验是否为高级检索字符串
     * @param {string} templateStr
     * @return {boolean}
     */
    isTemplateStr(templateStr) {
      return typeof templateStr === 'string' && this.isTempReg.test(templateStr)
    }
  
    /**
     * 将搜索语句转为便于渲染的数组
     * @param {string} templateStr
     * @return {Array}
     */
    parse2Arr(templateStr) {
      const ast = this.str2Ast(templateStr)
      let list :any= []
      ast.children.forEach((el:any) => {
        if (el.type == 'group') {
          let group:any = []
          el.children.forEach((el2:any) => {
            if (el2.type === 'item') {
              // el2.value = el2.value.replace(/(&|\||\(|\))/g, '').trim()
              group.push(el2)
            } else if (el2.type === 'logic') {
              let last = group[group.length - 1]
              last && (last.logic = el2.value)
            }
          })
          if (!group.length) {
            group = [{ key: '', value: '', matcher: '' }]
          }
          list.push(group)
        } else if (el.type == 'logic') {
          let last = list[list.length - 1]
          last && (last.logic = el.value)
        }
      })
      if (!list.length) {
        list = [[{ key: '', value: '', matcher: '' }]]
      }
      return list
    }
  
    /**
     * 字符串转语法树
     * @param {string} templateStr
     * @returns {AST} 解析后的语法树
     */
    str2Ast(templateStr) {
      templateStr = templateStr
        .replace(/\s*(&&|\|\|)\s*/g, (w) => {
          return w.trim()
        })
        .trim()
      let stack:any = []
      let rest = templateStr
      let index = 0
      while (index < templateStr.length) {
        rest = templateStr.substr(index)
        if (/^(&&|\|\|)/.test(rest)) {
          // 匹配以&& 或 || 开头的  转为逻辑
          const logic = rest.match(/^(&&|\|\|)/)[0]
          if (
            stack[stack.length - 1] &&
            stack[stack.length - 1].type === 'logic'
          ) {
            // 如果存在连续逻辑符取最后的一个
            stack[stack.length - 1].value = logic
            index += logic.length
            continue
          }
          stack.push(this.escapeLogic(logic))
          index += logic.length
        } else if (/^\(.*?\)(?=&&|\|\|)/.test(rest)) {
          // 匹配以'('开头以 ')&&' 或 ')||' 结尾的组
          const groupStr = rest.match(/^\(.*?\)(?=&&|\|\|)/)[0]
          stack.push(this.escapeGroup(groupStr))
          index += groupStr.length
        } else if (/^\(.*\)$/.test(rest)) {
          // 匹配以'(' 开头 以')'收尾的 组
          const groupStr = rest.match(/^\(.*\)$/)[0]
          stack.push(this.escapeGroup(groupStr))
          index += groupStr.length
        } else if (/^.*?(?=&&|\|\|)/.test(rest)) {
          // 匹配以 && 或 || 结尾的无括号组
          const groupStr = rest.match(/^.*?(?=&&|\|\|)/)[0]
          stack.push(this.escapeGroup(groupStr))
          index += groupStr.length
        } else {
          // 匹配最后剩下的无括号组
          stack.push(this.escapeGroup(rest))
          index += rest.length
        }
      }
      return {
        type: 'root',
        children: stack,
      }
    }
  
    /**
     * 语法树转字符
     * @param {AST} ast
     * @returns {string}
     */
    ast2Str(ast) {
      if (Object.prototype.toString.apply(ast) !== '[object Object]') {
        throw Error('语法树格式错误')
      }
      if (ast.type !== 'root' && !ast.children.length) {
        return ''
      }
      let str = ''
      ast.children.forEach((el) => {
        if (el.type === 'group') {
          const groupStr = el.children.reduce((t, c) => {
            if (c.type === 'item') {
              return (t += `${c.key}${c.matcher}${c.value}`)
            } else if (c.type === 'logic') {
              return (t += c.value)
            } else {
              return t
            }
          }, '')
          str += `(${groupStr})`
        } else if (el.type === 'logic') {
          str += el.value
        }
      })
      return str
    }
  
    // 匹配对应的匹配符
    matchMatcher(str) {
      let regList = [
        /^===/,
        /^==!/,
        /^==/,
        /^=!\^/,
        /^=!\$/,
        /^=\*!/,
        /^=\*/,
        /^=!/,
        /^=\^/,
        /^=\$/,
        /^=/,
      ]
      for (let reg of regList) {
        if (reg.test(str)) {
          return str.match(reg)[0]
        }
      }
    }
  
    // 匹配对应的逻辑符
    matchLogic(str) {
      let regList = [/^&&/, /^\|\|/]
      for (let reg of regList) {
        if (reg.test(str)) {
          return str.match(reg)[0]
        }
      }
    }
  
    // 转义组
    escapeGroup(str) {
        let stack:any = []
      const group:any = {
        type: 'group',
        children: [],
      }
      if (/^\(.*\)$/.test(str)) {
        // 被括号包裹的组去掉括号再解析
        str = str.match(/^\((.*)\)$/)[1]
      }
      let index = 0
      let rest = str
      while (index < str.length) {
        rest = str.substr(index)
        if (/^(&&|\|\|)/.test(rest)) {
          // 匹配是否以逻辑符开头
          const logic = rest.match(/^(&&|\|\|)/)[0]
          if (
            group.children[group.children.length - 1] &&
            group.children[group.children.length - 1].type === 'logic'
          ) {
            // 如果存在连续逻辑符取最后的一个
            stack[stack.length - 1].value = logic
            index += logic.length
            continue
          }
          group.children.push(this.escapeLogic(logic))
          index += logic.length
         
        } else if (/^.+?(?=(&&|\|\|))/.test(rest)) {
          // 匹配后面有逻辑符的项
          const itemStr = rest.match(/^.+?(?=(&&|\|\|))/)[0]
          group.children.push(this.escapeItem(itemStr))
          index += itemStr.length
        } else {
         
          // 剩下的为结尾项
          group.children.push(this.escapeItem(rest))
          index += rest.length
        }
      }
      return group
    }
  
    // 转义搜索键值对
    escapeItem(str) {
      let rest = str
      let key = ''
      let value = ''
      let matcher = ''
      let index = 0
      while (index < str.length) {
        rest = str.substr(index)
        if (this.matcherReg.test(rest)) {
          matcher = this.matchMatcher(rest)
          index += matcher.length
          value = str.substr(index)
          break
        } else {
          key += str[index]
          index++
        }
      }
      return {
        type: 'item',
        matcher,
        key: key.trim(),
        value: value.trim(),
      }
    }
    // 转义逻辑关系
    escapeLogic(logic) {
      return {
        type: 'logic',
        value: logic,
      }
    }
  }
  
  export default new AdvancedSearch()
  